﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using Awesomium.Core;

namespace RSuite.Services
{
    public abstract class LocalServiceBase
    {
        protected ProgressManager progress;

        public WebHost Browser
        {
            get;
            set;
        }

        public LocalServiceBase()
        {
            this.progress = ProgressManager.Current;
            this.progress.UpdateTask = this.UpdateTask;
        }

        public string ToJSON(object value)
        {
            return new JavaScriptSerializer().Serialize(value);
        }

        public object FromJSON(string json)
        {
            return new JavaScriptSerializer().Deserialize<dynamic>(json);
        }

        public object InvokeMethod(string method, string parameters, string taskId)
        {
            var paramObj = this.FromJSON(parameters);
            var result = this.Process(method, paramObj, taskId);
            if (result == null)
            {
                result = this.ProcessInternal(method, paramObj, taskId);
            }

            return result;
        }

        public delegate object AsyncProc();

        protected object Async(string taskId, AsyncProc proc)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object data)
            {
                this.progress.Add(taskId);
                try
                {
                    var result = proc();
                    this.FinishTask(taskId, result);
                }
                finally
                {
                    this.progress.Remove();
                }
            }));

            return null;
        }

        protected object ProcessInternal(string method, dynamic parameters, string taskId)
        {
            switch (method)
            {
                case "update-window":
                    var form = this.Browser.FindForm();
                    if (this.HasAttr(parameters, "title"))
                    {
                        form.Text = parameters.title;
                    }
                    if (this.HasAttr(parameters, "width"))
                    {
                        form.Width = int.Parse(parameters.width);
                    }
                    if (this.HasAttr(parameters, "height"))
                    {
                        form.Height = int.Parse(parameters.height);
                    }
                    if (this.HasAttr(parameters, "left"))
                    {
                        form.Left = int.Parse(parameters.left);
                    }
                    if (this.HasAttr(parameters, "top"))
                    {
                        form.Top = int.Parse(parameters.top);
                    }
                    return null;
                case "navigate":
                    this.Browser.Navigate(parameters);
                    return null;
                case "alert":
                    MessageBox.Show(parameters);
                    return null;
                case "browse-folder":
                case "browse-dialog":
                    var dialog = new FolderBrowserDialog();
                    dialog.SelectedPath = parameters;
                    if (dialog.ShowDialog() != DialogResult.OK)
                    {
                        return string.Empty;
                    }

                    return dialog.SelectedPath;
                case "open-folder":
                    if (Directory.Exists(parameters))
                    {
                        System.Diagnostics.Process.Start(parameters);
                    }
                    else if (File.Exists(parameters))
                    {
                        System.Diagnostics.Process.Start("explorer.exe", string.Format("/select,\"{0}\"", parameters));
                    }
                    return null;
                default:
                    return null;
            }
        }

        protected bool HasAttr(object obj, string attr)
        {
            if (obj == null)
            {
                return false;
            }
            else
            {
                return ((IDictionary<String, object>)obj).ContainsKey(attr);
            }
        }

        protected abstract object Process(string method, dynamic parameters, string taskId);

        public void Bind(WebHost browser)
        {
            this.Browser = browser;
            this.Browser.LoadCompleted += new EventHandler(delegate(object sender, EventArgs e)
            {
                this.Browser.CreateObject("Awesomium");
                this.Browser.SetObjectCallback("Awesomium", "InvokeMethod", new JSCallback(delegate(object sender2, JSCallbackEventArgs e2)
                {
                    var result = this.InvokeMethod(e2.Arguments[0].ToString(), e2.Arguments[1].ToString(), e2.Arguments[2].ToString());
                    if (result != null)
                    {
                        this.FinishTask(e2.Arguments[2].ToString(), result);
                    }
                }));
            });
        }

        public void FinishTask(string taskId, object data)
        {
            if (this.Browser.InvokeRequired)
            {
                this.Browser.BeginInvoke(new TaskDelegate(this.FinishTask), taskId, data);
            }
            else
            {
                this.Browser.CallJavascriptFunction("Async", "finishTask", new JSValue(taskId), new JSValue(this.ToJSON(data)));
            }
        }

        public void UpdateTask(string taskId, object data)
        {
            if (this.Browser.InvokeRequired)
            {
                this.Browser.BeginInvoke(new TaskDelegate(this.UpdateTask), taskId, data);
            }
            else
            {
                this.Browser.CallJavascriptFunction("Async", "updateTask", new JSValue(taskId), new JSValue(this.ToJSON(data)));
            }
        }

        public delegate void TaskDelegate(string taskId, object data);
    }

    public class ParamReader : IDisposable
    {
        private dynamic parameters;

        public ParamReader(dynamic parameters)
        {
            this.parameters = parameters;
        }

        public bool HasAttr(string name)
        {
            return ((IDictionary<string, object>)parameters).ContainsKey(name);
        }

        public string Get(string name)
        {
            return (string)this.parameters[name];
        }

        public string[] GetArray(string name)
        {
            return ((object[])this.parameters[name]).Cast<string>().ToArray();
        }

        public bool GetBool(string name)
        {
            return (bool)this.parameters[name];
        }

        public int GetInt(string name)
        {
            return (int)this.parameters[name];
        }

        public void Dispose()
        {
            this.parameters = null;
        }
    }
}